import pygame as pg
import os,sys
import random,math

if sys.version_info[0] == 2: range = xrange #version compatibility.

class _Shape(object):
    def __init__(self):
        self.time_based = False
        self.timer = 0.0
##        self.time_step = .0301
        self.time_step = .0001
        self.timer_max = 1.0
    def scale(self,size,*args):
        return [(arg[0]//size[0],arg[1]//size[1]) for arg in args]
    def get_parameter(self):
        if not self.time_based:
            return random.random()
        else:
            self.timer = (self.timer+self.time_step)%self.timer_max
            return self.timer

class Ellipse(_Shape):
    def __init__(self,size,cent,rad,width=None):
        _Shape.__init__(self)
        width = width if width else rad
        (self.cent,self.rad,self.width) = self.scale(size,cent,rad,width)
        self.time_based = True
        self.frame_points = set()

    def generate_points(self):
        t = self.get_parameter()
        r = 0
        r_resolution = 0.008
        points = set()
        x_trig,y_trig = math.cos(2*math.pi*t),-math.sin(2*math.pi*t)
        while r < 1:
            x = self.cent[0]+int((self.rad[0]-self.width[0]*r)*x_trig)
            y = self.cent[1]+int((self.rad[1]-self.width[1]*r)*y_trig)
            if (x,y) not in self.frame_points:
                points.add((x,y))
            r += r_resolution
        self.frame_points |= points
        return points

class MiscTrig(_Shape):
    def __init__(self,size,cent,rad,width=None):
        _Shape.__init__(self)
        width = width if width else rad
        (self.cent,self.rad,self.width) = self.scale(size,cent,rad,width)
        self.time_based = True
        self.frame_points = set()
        self.cos_mag = 30
        self.sin_mag = 10
    def generate_points(self):
        t = self.get_parameter()
        r = 0
        points = set()
        x_trig,y_trig = math.cos(self.cos_mag*math.pi*t),-math.sin(self.sin_mag*math.pi*t)
        x = self.cent[0]+int((self.rad[0]-self.width[0]*t)*x_trig)
        y = self.cent[1]-int((self.rad[1]-self.width[1]*t)*y_trig)
        points |= set(((x,y),))
        self.frame_points |= points
        return points

class Control(object):
    def __init__(self,size):
        os.environ["SDL_VIDEO_CENTERED"] = '1'
        pg.init()
        self.Clock = pg.time.Clock()
        pg.display.set_caption("FPS: {:.2f}".format(self.Clock.get_fps()))
        self.screen = pg.display.set_mode(size)
        self.fps = 60
        self.done = False
        self.count = 0

        self.cell_size = (1,1)
        self.cells_per_frame = 50
        self.Shape = Ellipse(self.cell_size,(150,300),(100,200),(10,100))
##        self.Shape = MiscTrig(self.cell_size,(150,300),(100,250))#,(10,9))
        self.color_speed = 0.25
        self.color_gen = self.smooth_color()

    def event_loop(self):
        keys = pg.key.get_pressed()
        for event in pg.event.get():
            if event.type == pg.QUIT or keys[pg.K_ESCAPE]:
                self.done = True
            elif event.type == pg.KEYDOWN:
                if event.key == pg.K_SPACE:
                    self.screen.fill(0)

    def update(self):
##        self.screen.fill(0)
        for i in range(self.cells_per_frame):
            color = next(self.color_gen)
            points = self.Shape.generate_points()
            for x,y in points:
                self.screen.fill(color,((x*self.cell_size[0],y*self.cell_size[1]),self.cell_size))
                self.count+=1

    def main(self):
        while not self.done:
            self.event_loop()
            self.update()
            pg.display.update()
            pg.display.set_caption("FPS: {:.2f}".format(self.Clock.get_fps()))
            self.Clock.tick(self.fps)
##            print(self.count)
            self.count = 0
            self.Shape.frame_points = set()

    def smooth_color(self,mini=0,maxi=255):
        up = True
        ind = 1
        color = [maxi,mini,mini]
        while 1:
            if up:
                color[ind] = min(color[ind]+self.color_speed,maxi)
            else:
                color[ind] = max(color[ind]-self.color_speed,mini)
            if color[ind] in (maxi,mini):
                up = not up
                ind = (ind-1)%3
            yield color

if __name__ == "__main__":
    SIZE = (300,600)
    RunIt = Control(SIZE)
    RunIt.main()
    pg.quit();sys.exit()